package by.ad.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.dom.DOMSource;
import net.sf.saxon.Configuration;
import net.sf.saxon.TransformerFactoryImpl;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.ext.EntityResolver2;
import by.ad.Utils;

public abstract class XmlUtils
{
  public static final String MODEL_SCHEME = "http://schemas.ad.by/model/2010".intern();
  
  // ISO8601 UTC DateFormat
  @SuppressWarnings("serial")
  public static final class ISO8601UTCFormat extends SimpleDateFormat {
    // Atom feed date format
    public static final String PATTERN_ISO8601UTC = "yyyy-MM-dd'T'HH:mm:ss'Z'".intern();
    
    public ISO8601UTCFormat() {
      super(PATTERN_ISO8601UTC);
      setTimeZone(TimeZone.getTimeZone("GMT"));
    }
  }
  
  private static final Logger log = Logger.getLogger(XmlUtils.class.getName());
  
  // load xml 
  public static Source loadXml(final InputSource inputSource) 
    throws SAXException, ParserConfigurationException, IOException 
  {
    log.info("inputSource.getSystemId()='" + inputSource.getSystemId() + "', getPublicId()='" + inputSource.getPublicId() + "'");
    
    final DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
    db.setEntityResolver(new EntityResolver2() {
      public InputSource resolveEntity(final String publicId, final String systemId) throws SAXException, IOException { return null; }
      public InputSource getExternalSubset(final String name, final String baseURI) throws SAXException, IOException { return null; }

      public InputSource resolveEntity(final String name, final String publicId, 
        final String baseURI, final String systemId) throws SAXException, IOException
      {
        try {
          return XmlUtils.getInputSource(new URI(baseURI).resolve(systemId).getPath());
        } catch (URISyntaxException e) {
          throw new IOException(e);
        }
      }
    });
    final Document doc = db.parse(inputSource);
    return new DOMSource(doc, inputSource.getSystemId());
  }

  public static Source loadXml(final String relativeXmlFileName) 
    throws SAXException, ParserConfigurationException, IOException, URISyntaxException 
  {
    return loadXml(getInputSource(relativeXmlFileName));
  }

  // common uri resolver
  public static final URIResolver uriResolver = new URIResolver() {
    public Source resolve(final String href, final String base)
      throws TransformerException
    {
      try {
        return loadXml(new URI(base).resolve(href).getPath());
      } catch (URISyntaxException e) {
        throw new TransformerException(e);
      } catch (FileNotFoundException e) {
        throw new TransformerException(e);
      } catch (SAXException e) {
        throw new TransformerException(e);
      } catch (ParserConfigurationException e) {
        throw new TransformerException(e);
      } catch (IOException e) {
        throw new TransformerException(e);
      }
    }
  };
  
  // load xml as xslt template
  public static Templates loadXsltTemplate(final Source source)
    throws TransformerConfigurationException
  {
    final TransformerFactory factory = TransformerFactory.newInstance();
    factory.setURIResolver(uriResolver);

    final Configuration config = ((TransformerFactoryImpl) factory).getConfiguration();
    config.registerExtensionFunction(new SaxonNodeSet());

    return factory.newTemplates(source);
  }
  
  // xslt transformer
  public static Transformer createTransformer(final String relativeXsltFileName)
    throws TransformerConfigurationException, SAXException, ParserConfigurationException, IOException, URISyntaxException 
  {
    final Transformer transformer = loadXsltTemplate(
      loadXml(relativeXsltFileName)).newTransformer();
    transformer.setURIResolver(uriResolver);
    transformer.setParameter("server-side-transformation", true);
    
    return transformer;
  }
  
  public static InputSource getInputSource(final String fileName) 
    throws FileNotFoundException, URISyntaxException 
  {
    final String fileNameN = new URI(fileName).getPath();
    final File file = new File("WEB-INF" + fileNameN);
    //log.info("fileName='" + fileName + "', fileNameN='" + fileNameN + "', file='" + file.getAbsolutePath() + "', fileAccessible=" + Utils.fileAccessible(file));
    final InputSource source = new InputSource( 
      Utils.fileAccessible(file)
        ? new FileInputStream(file)
        : uriResolver.getClass().getResourceAsStream(fileNameN)
    );
    
    source.setSystemId(fileNameN);
    source.setPublicId(fileName);
    return source;
  } 
      
}
